]> git.neil.brown.name Git - wiggle.git/blob - vpatch.c
Release 0.8
[wiggle.git] / vpatch.c
1 /*
2  * wiggle - apply rejected patches
3  *
4  * Copyright (C) 2005 Neil Brown <neilb@cse.unsw.edu.au>
5  *
6  *
7  *    This program is free software; you can redistribute it and/or modify
8  *    it under the terms of the GNU General Public License as published by
9  *    the Free Software Foundation; either version 2 of the License, or
10  *    (at your option) any later version.
11  *
12  *    This program is distributed in the hope that it will be useful,
13  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *    GNU General Public License for more details.
16  *
17  *    You should have received a copy of the GNU General Public License
18  *    along with this program; if not, write to the Free Software
19  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  *    Author: Neil Brown
22  *    Email: <neilb@suse.de>
23  */
24
25 /*
26  * vpatch - visual front end for wiggle
27  *
28  * "files" display, lists all files with statistics
29  *    - can hide various lines including subdirectories
30  *      and files without wiggles or conflicts
31  * "diff" display shows merged file with different parts
32  *      in different colours
33  *    - untouched are pale  A_DIM
34  *    - matched/remaining are regular A_NORMAL
35  *    - matched/removed are red/underlined A_UNDERLINE
36  *    - unmatched in file are A_STANDOUT
37  *    - unmatched in patch are A_STANDOUT|A_UNDERLINE ???
38  *    - inserted are inverse/green ?? A_REVERSE
39  *
40  *  The window can be split horiz or vert and two different
41  *  views displayed.  They will have different parts missing
42  *
43  *  So a display of NORMAL, underline, standout|underline reverse
44  *   should show a normal patch.
45  *
46  */
47
48 #include "wiggle.h"
49 #include <malloc.h>
50 #include <string.h>
51 #include <curses.h>
52 #include <unistd.h>
53 #include <stdlib.h>
54 #include <signal.h>
55 #include <fcntl.h>
56
57 #define assert(x) do { if (!(x)) abort(); } while (0)
58
59 struct plist {
60         char *file;
61         unsigned int start, end;
62         int parent;
63         int next, prev, last;
64         int open;
65         int chunks, wiggles, conflicts;
66         int calced;
67 };
68
69 struct plist *patch_add_file(struct plist *pl, int *np, char *file,
70                unsigned int start, unsigned int end)
71 {
72         /* size of pl is 0, 16, n^2 */
73         int n = *np;
74         int asize;
75
76         while (*file == '/') file++; /* leading '/' are bad... */
77
78 /*      printf("adding %s at %d: %u %u\n", file, n, start, end); */
79         if (n==0) asize = 0;
80         else if (n<=16) asize = 16;
81         else if ((n&(n-1))==0) asize = n;
82         else asize = n+1; /* not accurate, but not too large */
83         if (asize <= n) {
84                 /* need to extend array */
85                 struct plist *npl;
86                 if (asize < 16) asize = 16;
87                 else asize += asize;
88                 npl = realloc(pl, asize * sizeof(struct plist));
89                 if (!npl) {
90                         fprintf(stderr, "malloc failed - skipping %s\n", file);
91                         return pl;
92                 }
93                 pl = npl;
94         }
95         pl[n].file = file;
96         pl[n].start = start;
97         pl[n].end = end;
98         pl[n].last = pl[n].next = pl[n].prev = pl[n].parent = -1;
99         pl[n].chunks = pl[n].wiggles = 0; pl[n].conflicts = 100;
100         pl[n].open = 1;
101         pl[n].calced = 0;
102         *np = n+1;
103         return pl;
104 }
105
106
107
108 struct plist *parse_patch(FILE *f, FILE *of, int *np)
109 {
110         /* read a multi-file patch from 'f' and record relevant
111          * details in a plist.
112          * if 'of' >= 0, fd might not be seekable so we write
113          * to 'of' and use lseek on 'of' to determine position
114          */
115         struct plist *plist = NULL;
116
117         while (!feof(f)) {
118                 /* first, find the start of a patch: "\n+++ "
119                  * grab the file name and scan to the end of a line
120                  */
121                 char *target="\n+++ ";
122                 char *target2="\n--- ";
123                 char *pos = target;
124                 int c;
125                 char name[1024];
126                 unsigned start, end;
127
128                 while (*pos && (c=fgetc(f)) != EOF ) {
129                         if (of) fputc(c, of);
130                         if (c == *pos)
131                                 pos++;
132                         else pos = target;
133                 }
134                 if (c == EOF)
135                         break;
136                 assert(c == ' ');
137                 /* now read a file name */
138                 pos = name;
139                 while ((c=fgetc(f)) != EOF && c != '\t' && c != '\n' && c != ' ' &&
140                        pos - name < 1023) {
141                         *pos++ = c;
142                         if (of) fputc(c, of);
143                 }
144                 *pos = 0;
145                 if (c == EOF)
146                         break;
147                 if (of) fputc(c, of);
148                 while (c != '\n' && (c=fgetc(f)) != EOF) {
149                         if (of) fputc(c, of);
150                 }
151                 start = of ? ftell(of) : ftell(f);
152
153                 if (c == EOF) break;
154
155                 /* now skip to end - "\n--- " */
156                 pos = target2+1;
157
158                 while (*pos && (c=fgetc(f)) != EOF) {
159                         if (of) fputc(c, of);
160                         if (c == *pos)
161                                 pos++;
162                         else pos = target2;
163                 }
164                 end = of ? ftell(of) : ftell(f);
165                 if (pos > target2)
166                         end -= (pos - target2) - 1;
167                 plist = patch_add_file(plist, np,
168                                        strdup(name), start, end);
169         }
170         return plist;
171 }
172 #if 0
173 void die()
174 {
175         fprintf(stderr,"vpatch: fatal error\n");
176         abort();
177         exit(3);
178 }
179 #endif
180
181 static struct stream load_segment(FILE *f,
182                                   unsigned int start, unsigned int end)
183 {
184         struct stream s;
185         s.len = end - start;
186         s.body = malloc(s.len);
187         if (s.body) {
188                 fseek(f, start, 0);
189                 if (fread(s.body, 1, s.len, f) != s.len) {
190                         free(s.body);
191                         s.body = NULL;
192                 }
193         } else
194                 die();
195         return s;
196 }
197
198
199 void catch(int sig)
200 {
201         if (sig == SIGINT) {
202                 signal(sig, catch);
203                 return;
204         }
205         nocbreak();nl();endwin();
206         printf("Died on signal %d\n", sig);
207         exit(2);
208 }
209
210 int pl_cmp(const void *av, const void *bv)
211 {
212         const struct plist *a = av;
213         const struct plist *b = bv;
214         return strcmp(a->file, b->file);
215 }
216
217 int common_depth(char *a, char *b)
218 {
219         /* find number of patch segments that these two have
220          * in common
221          */
222         int depth = 0;
223         while(1) {
224                 char *c;
225                 int al, bl;
226                 c = strchr(a, '/');
227                 if (c) al = c-a; else al = strlen(a);
228                 c = strchr(b, '/');
229                 if (c) bl = c-b; else bl = strlen(b);
230                 if (al == 0 || al != bl || strncmp(a,b,al) != 0)
231                         return depth;
232                 a+= al;
233                 while (*a=='/') a++;
234                 b+= bl;
235                 while(*b=='/') b++;
236
237                 depth++;
238         }
239 }
240
241 struct plist *add_dir(struct plist *pl, int *np, char *file, char *curr)
242 {
243         /* any parent of file that is not a parent of curr
244          * needs to be added to pl
245          */
246         int d = common_depth(file, curr);
247         char *buf = curr;
248         while (d) {
249                 char *c = strchr(file, '/');
250                 int l;
251                 if (c) l = c-file; else l = strlen(file);
252                 file += l;
253                 curr += l;
254                 while (*file == '/') file++;
255                 while (*curr == '/') curr++;
256                 d--;
257         }
258         while (*file) {
259                 if (curr > buf && curr[-1] != '/')
260                         *curr++ = '/';
261                 while (*file && *file != '/')
262                         *curr++ = *file++;
263                 while (*file == '/') file++;
264                 *curr = '\0';
265                 if (*file)
266                         pl = patch_add_file(pl, np, strdup(buf),
267                                             0, 0);
268         }
269         return pl;
270 }
271
272 struct plist *sort_patches(struct plist *pl, int *np)
273 {
274         /* sort the patches, add directory names, and re-sort */
275         char curr[1024];
276         char *prev;
277         int parents[100];
278         int prevnode[100];
279         int i, n;
280         qsort(pl, *np, sizeof(struct plist), pl_cmp);
281         curr[0] = 0;
282         n = *np;
283         for (i=0; i<n; i++)
284                 pl = add_dir(pl, np, pl[i].file, curr);
285
286         qsort(pl, *np, sizeof(struct plist), pl_cmp);
287
288         /* array is now stable, so set up parent pointers */
289         n = *np;
290         curr[0] = 0;
291         prevnode[0] = -1;
292         prev = "";
293         for (i=0; i<n; i++) {
294                 int d = common_depth(prev, pl[i].file);
295                 if (d == 0)
296                         pl[i].parent = -1;
297                 else {
298                         pl[i].parent = parents[d-1];
299                         pl[pl[i].parent].last = i;
300                 }
301                 pl[i].prev = prevnode[d];
302                 if (pl[i].prev > -1)
303                         pl[pl[i].prev].next = i;
304                 prev = pl[i].file;
305                 parents[d] = i;
306                 prevnode[d] = i;
307                 prevnode[d+1] = -1;
308         }
309         return pl;
310 }
311
312 int get_strip(char *file)
313 {
314         int fd;
315         int strip = 0;
316
317         while (file && *file) {
318                 fd  = open(file, O_RDONLY);
319                 if (fd >= 0) {
320                         close(fd);
321                         return strip;
322                 }
323                 strip++;
324                 file = strchr(file, '/');
325                 if (file)
326                         while(*file == '/')
327                                 file++;
328         }
329         return -1;
330
331 }
332 int set_prefix(struct plist *pl, int n, int strip)
333 {
334         int i;
335         for(i=0; i<4 && i<n  && strip < 0; i++)
336                 strip = get_strip(pl[i].file);
337
338         if (strip < 0) {
339                 fprintf(stderr, "%s: Cannot file files to patch: please specify --strip\n",
340                         Cmd);
341                 return 0;
342         }
343         for (i=0; i<n; i++) {
344                 char *p = pl[i].file;
345                 int j;
346                 for (j=0; j<strip; j++) {
347                         if (p) p = strchr(p,'/');
348                         while (p && *p == '/') p++;
349                 }
350                 if (p == NULL) {
351                         fprintf(stderr, "%s: cannot strip %d segments from %s\n",
352                                 Cmd, strip, pl[i].file);
353                         return 0;
354                 }
355                 pl[i].file = p;
356         }
357         return 1;
358 }
359
360
361 int get_prev(int pos, struct plist *pl, int n)
362 {
363         if (pos == -1) return pos;
364         if (pl[pos].prev == -1)
365                 return pl[pos].parent;
366         pos = pl[pos].prev;
367         while (pl[pos].open &&
368                pl[pos].last >= 0)
369                 pos = pl[pos].last;
370         return pos;
371 }
372
373 int get_next(int pos, struct plist *pl, int n)
374 {
375         if (pos == -1) return pos;
376         if (pl[pos].open) {
377                 if (pos +1 < n)
378                         return pos+1;
379                 else
380                         return -1;
381         }
382         while (pos >= 0 && pl[pos].next == -1)
383                 pos = pl[pos].parent;
384         if (pos >= 0)
385                 pos = pl[pos].next;
386         return pos;
387 }
388
389 /* global attributes */
390 int a_delete, a_added, a_common, a_sep, a_void, a_unmatched, a_extra, a_already;
391
392 void draw_one(int row, struct plist *pl, FILE *f, int reverse)
393 {
394         char hdr[10];
395         hdr[0] = 0;
396
397         if (pl == NULL) {
398                 move(row,0);
399                 clrtoeol();
400                 return;
401         }
402         if (pl->calced == 0 && pl->end) {
403                 /* better load the patch and count the chunks */
404                 struct stream s1, s2;
405                 struct stream s = load_segment(f, pl->start, pl->end);
406                 struct stream sf = load_file(pl->file);
407                 if (reverse)
408                         pl->chunks = split_patch(s, &s2, &s1);
409                 else
410                         pl->chunks = split_patch(s, &s1, &s2);
411
412                 if (sf.body == NULL) {
413                         pl->wiggles = pl->conflicts = -1;
414                 } else {
415                         struct file ff, fp1, fp2;
416                         struct csl *csl1, *csl2;
417                         struct ci ci;
418                         ff = split_stream(sf, ByWord, 0);
419                         fp1 = split_stream(s1, ByWord, 0);
420                         fp2 = split_stream(s2, ByWord, 0);
421                         csl1 = pdiff(ff, fp1, pl->chunks);
422                         csl2 = diff(fp1,fp2);
423                         ci = make_merger(ff, fp1, fp2, csl1, csl2, 0, 1);
424                         pl->wiggles = ci.wiggles;
425                         pl->conflicts = ci.conflicts;
426                         free(csl1);
427                         free(csl2);
428                         free(ff.list);
429                         free(fp1.list);
430                         free(fp2.list);
431                 }
432
433                 free(s1.body);
434                 free(s2.body);
435                 free(s.body);
436                 pl->calced = 1;
437         }
438         if (pl->end == 0) {
439                 strcpy(hdr, "         ");
440         } else {
441                 if (pl->chunks > 99)
442                         strcpy(hdr, "XX");
443                 else sprintf(hdr, "%2d", pl->chunks);
444                 if (pl->wiggles > 99)
445                         strcpy(hdr+2, " XX");
446                 else sprintf(hdr+2, " %2d", pl->wiggles);
447                 if (pl->conflicts > 99)
448                         strcpy(hdr+5, " XX ");
449                 else sprintf(hdr+5, " %2d ", pl->conflicts);
450         }
451         if (pl->end)
452                 strcpy(hdr+9, "= ");
453         else if (pl->open)
454                 strcpy(hdr+9, "+ ");
455         else strcpy(hdr+9, "- ");
456
457         mvaddstr(row, 0, hdr);
458         mvaddstr(row, 11, pl->file);
459         clrtoeol();
460 }
461
462 void addword(struct elmnt e)
463 {
464         addnstr(e.start, e.len);
465 }
466 void addsep(struct elmnt e1, struct elmnt e2)
467 {
468         int a,b,c,d,e,f;
469         char buf[50];
470         attron(a_sep);
471         sscanf(e1.start+1, "%d %d %d", &a, &b, &c);
472         sscanf(e2.start+1, "%d %d %d", &d, &e, &f);
473         sprintf(buf, "@@ -%d,%d +%d,%d @@\n", b,c,e,f);
474         addstr(buf);
475         attroff(a_sep);
476 }
477 #define BEFORE  1
478 #define AFTER   2
479 #define ORIG    4
480 #define RESULT  8
481 #define CHANGED 32 /* The RESULT is different to ORIG */
482 #define CHANGES 64 /* AFTER is different to BEFORE */
483
484 struct pos {
485         int a,b,c;
486 };
487 struct elmnt prev_elmnt(struct pos *pos, int mode,
488                      struct file f1, struct file f2, struct csl *csl)
489 {
490         while(1) {
491                 int a1, b1;
492                 if (pos->a > csl[pos->c].a) {
493                         assert(pos->b > csl[pos->c].b);
494                         pos->a--;
495                         pos->b--;
496                         return f1.list[pos->a];
497                 }
498                 b1=0;
499                 if (pos->c) b1 = csl[pos->c-1].b+csl[pos->c-1].len;
500                 if (pos->b > b1) {
501                         pos->b--;
502                         if (!(mode&AFTER))
503                                 continue;
504                         return f2.list[pos->b];
505                 }
506                 a1=0;
507                 if (pos->c) a1 = csl[pos->c-1].a+csl[pos->c-1].len;
508                 if (pos->a > a1) {
509                         pos->a--;
510                         if (!(mode&BEFORE))
511                                 continue;
512                         return f1.list[pos->a];
513                 }
514                 /* must be time to go to previous common segment */
515                 pos->c--;
516                 if (pos->c < 0) {
517                         struct elmnt e;
518                         e.start = NULL; e.len = 0;
519                         return e;
520                 }
521         }
522 }
523
524 struct elmnt next_elmnt(struct pos *pos, int mode, int *type,
525                      struct file f1, struct file f2, struct csl *csl)
526 {
527         if (pos->c < 0) {
528                 struct elmnt e;
529                 e.start = NULL; e.len = 0;
530                 return e;
531         }
532         while(1) {
533                 int a1;
534                 *type = a_delete;
535                 if (pos->a < csl[pos->c].a) {
536                         if (mode & BEFORE)
537                                 return f1.list[pos->a++];
538                         pos->a++;
539                         continue;
540                 }
541                 *type = a_added;
542                 if (pos->b < csl[pos->c].b) {
543                         if (mode & AFTER)
544                                 return f2.list[pos->b++];
545                         pos->b++;
546                         continue;
547                 }
548                 *type = a_common;
549                 a1 = csl[pos->c].a + csl[pos->c].len;
550                 if (pos->a < a1) {
551                         pos->b++;
552                         return f1.list[pos->a++];
553                 }
554                 if (csl[pos->c].len == 0) {
555                         struct elmnt e;
556                         e.start = NULL; e.len = 0;
557                         pos->c = -1;
558                         return e;
559                 }
560                 pos->c++;
561         }
562 }
563
564 void pos_sol(struct pos *pos, int mode, struct file f1, struct file f2, struct csl *csl)
565 {
566         /* find the start-of-line before 'pos' considering 'mode' only.
567          */
568         if (pos->c < 0) return;
569         while(1) {
570                 struct pos tpos = *pos;
571                 struct elmnt e = prev_elmnt(&tpos, mode, f1, f2, csl);
572                 if (e.start == NULL) {
573                         return;
574                 }
575                 if (e.start[0] == '\n' || e.start[0] == 0)
576                         return;
577                 *pos = tpos;
578         }
579 }
580
581 void prev_pos(struct pos *pos, int mode, struct file f1, struct file f2, struct csl *csl)
582 {
583         /* find the start-of-line before 'pos' considering 'mode' only.
584          */
585         int eol=0;
586         if (pos->c < 0) return;
587         while(1) {
588                 struct pos tpos = *pos;
589                 struct elmnt e = prev_elmnt(&tpos, mode, f1, f2, csl);
590                 if (e.start == NULL) {
591                         pos->c = -1;
592                         return;
593                 }
594                 if (e.start[0] == '\n' || e.start[0] == 0) {
595                         if (eol == 1)
596                                 return;
597                         eol=1;
598                 }
599                 *pos = tpos;
600                 if (e.start[0] == 0) return;
601
602         }
603 }
604
605 void next_pos(struct pos *pos, int mode, struct file f1, struct file f2, struct csl *csl)
606 {
607         /* find the start-of-line after 'pos' considering 'mode' only.
608          */
609         int type;
610         if (pos->c < 0) return;
611         while(1) {
612                 struct pos tpos = *pos;
613                 struct elmnt e = next_elmnt(&tpos, mode, &type, f1, f2, csl);
614                 if (e.start == NULL) {
615                         pos->c = -1;
616                         return;
617                 }
618                 *pos = tpos;
619                 if (e.start[0] == '\n') {
620                         return;
621                 }
622                 if (e.start[0] == 0)
623                         return;
624         }
625 }
626
627 void draw_line(int i, struct pos pos, int mode,
628                struct file f1, struct file f2, struct csl *csl, int start, int len)
629 {
630         int col = 0;
631         int attr;
632         struct elmnt e1;
633         move(i, col);
634         while (1) {
635                 e1 = next_elmnt(&pos, mode, &attr, f1, f2, csl);
636                 if (e1.start == NULL) {
637                         attr = a_void;
638                         break;
639                 }
640                 if (e1.start[0] == '\0') {
641                         int a,b,c;
642                         int d,e,f;
643                         struct elmnt e2 = f2.list[pos.b-1];
644                         char buf[50];
645                         (void)attrset(a_sep);
646                         sscanf(e1.start+1, "%d %d %d", &a, &b, &c);
647                         sscanf(e2.start+1, "%d %d %d", &d, &e, &f);
648                         sprintf(buf, "@@ -%d,%d +%d,%d @@\n", b,c, e,f);
649                         addstr(buf);
650                         break;
651                 } else {
652                         unsigned char *c;
653                         int l;
654                         (void)attrset(attr);
655                         if (e1.start[0] == '\n') {
656                                 break;
657                         }
658                         c = (unsigned char *)e1.start;
659                         l = e1.len;
660                         while (l) {
661                                 if (*c >= ' ' && *c != 0x7f) {
662                                         /* normal width char */
663                                         if (col >= start && col < len+start)
664                                                 mvaddch(i,col-start, *c);
665                                         col++;
666                                 } else if (*c == '\t') {
667                                         do {
668                                                 if (col >= start && col < len+start)
669                                                         mvaddch(i, col-start, ' ');
670                                                 col++;
671                                         } while ((col&7)!= 0);
672                                 } else {
673                                         if (col>=start && col < len+start)
674                                                 mvaddch(i, col-start, *c=='\n'?'@':'?');
675                                         col++;
676                                 }
677                                 c++;
678                                 l--;
679                         }
680                 }
681         }
682         bkgdset(attr);
683         clrtoeol();
684         bkgdset(A_NORMAL);
685 }
686
687 void diff_window(struct plist *p, FILE *f)
688 {
689         /* display just the diff, either 'before' or 'after' or all.
690          *
691          * In any case highlighting is the same
692          *
693          * Current pos is given as a,b,csl where a and b are
694          * before or in the common segment, and one is  immediately
695          * after a newline.
696          * The current row is given by 'row'.
697          * Lines do not wrap, be horizontal scrolling is supported.
698          *
699          * We don't insist that the top line lies at or above the top
700          * of the screen, as that allows alignment of different views
701          * more easily.
702          */
703         struct stream s;
704         struct stream  s1, s2;
705         struct file f1, f2;
706         struct csl *csl;
707         char buf[100];
708         int ch;
709         int row, rows, cols;
710         int start = 0;
711         int i;
712         int c;
713         int refresh = 2;
714         int mode = BEFORE|AFTER;
715
716         struct pos pos, tpos;
717
718         s = load_segment(f, p->start, p->end);
719         ch = split_patch(s, &s1, &s2);
720
721
722         f1 = split_stream(s1, ByWord, 0);
723         f2 = split_stream(s2, ByWord, 0);
724
725         csl = diff(f1, f2);
726
727         pos.a=0; pos.b=0; pos.c=0;
728         row = 1;
729
730         while(1) {
731                 if (refresh == 2) {
732                         clear();
733                         sprintf(buf, "File: %s\n", p->file);
734                         attrset(A_BOLD); mvaddstr(0,0,buf); clrtoeol(); attrset(A_NORMAL);
735                         refresh = 1;
736                 }
737                 if (row < 1 || row >= rows)
738                         refresh = 1;
739                 if (refresh) {
740                         refresh = 0;
741                         getmaxyx(stdscr,rows,cols);
742                         if (row < -3)
743                                 row = (rows+1)/2;
744                         if (row < 1)
745                                 row = 1;
746                         if (row >= rows+3)
747                                 row = (rows+1)/2;
748                         if (row >= rows)
749                                 row = rows-1;
750                         tpos = pos;
751                         pos_sol(&tpos, mode, f1, f2, csl);
752                         draw_line(row, tpos, mode, f1, f2, csl, start, cols);
753                         for (i=row-1; i>=1; i--) {
754                                 prev_pos(&tpos, mode, f1,f2,csl);
755                                 draw_line(i, tpos, mode, f1, f2,csl, start,cols);
756                         }
757                         tpos = pos;
758                         for (i=row+1; i<rows; i++) {
759                                 next_pos(&tpos, mode, f1, f2, csl);
760                                 draw_line(i, tpos, mode, f1, f2, csl, start,cols);
761                         }
762                 }
763                 move(row,0);
764                 c = getch();
765                 switch(c) {
766                 case 27: /* escape */
767                         break;
768                 case 'q':
769                         return;
770                 case 'L'-64:
771                         refresh = 2;
772                         break;
773
774                 case 'j':
775                 case 'n':
776                 case 'N':
777                 case 'N'-64:
778                 case KEY_DOWN:
779                         tpos = pos;
780                         next_pos(&tpos, mode, f1, f2, csl);
781                         if (tpos.c >= 0) {
782                                 pos = tpos;
783                                 row++;
784                         }
785                         break;
786                 case 'k':
787                 case 'p':
788                 case 'P':
789                 case 'P'-64:
790                 case KEY_UP:
791                         tpos = pos;
792                         prev_pos(&tpos, mode, f1, f2, csl);
793                         if (tpos.c >= 0) {
794                                 pos = tpos;
795                                 row--;
796                         }
797                         break;
798
799                 case 'a':
800                         mode = AFTER;
801                         refresh = 1;
802                         break;
803                 case 'b':
804                         mode = BEFORE;
805                         refresh = 1;
806                         break;
807                 case 'x':
808                         mode = AFTER|BEFORE;
809                         refresh = 1;
810                         break;
811
812                 case 'h':
813                 case KEY_LEFT:
814                         if (start > 0) start--;
815                         refresh = 1;
816                         break;
817                 case 'l':
818                 case KEY_RIGHT:
819                         if (start < cols) start++;
820                         refresh = 1;
821                         break;
822                 }
823         }
824 }
825
826 struct mpos {
827         struct mp {
828                 int m; /* merger index */
829                 int s; /* stream 0,1,2 for a,b,c */
830                 int o; /* offset in that stream */
831         }       p, /* the current point */
832                 lo, /* eol for start of the current group */
833                 hi; /* eol for end of the current group */
834         int side; /* -1 if on the '-' of a diff,
835                    * +1 if on the '+',
836                    * 0 if on an unchanged (lo/hi not meaningful)
837                    */
838 };
839 int same_mpos(struct mpos a, struct mpos b)
840 {
841         return a.p.m == b.p.m &&
842                 a.p.s == b.p.s &&
843                 a.p.o == b.p.o &&
844                 a.side == b.side;
845 }
846
847 int invalid(int s, enum mergetype type)
848 {
849         switch(type) {
850         case End: return 0;
851         case Unmatched: return s>0;
852         case Unchanged: return s>0;
853         case Extraneous: return s<2;
854         case Changed: return s==1;
855         case Conflict: return 0;
856         case AlreadyApplied: return 0;
857         }
858         return 0;
859 }
860
861 struct elmnt next_melmnt(struct mpos *pos,
862                          struct file fm, struct file fb, struct file fa,
863                          struct merge *m)
864 {
865         int l;
866         pos->p.o++;
867         while(1) {
868                 if (pos->p.m < 0)
869                         l = 0;
870                 else switch(pos->p.s) {
871                 case 0: l = m[pos->p.m].al; break;
872                 case 1: l = m[pos->p.m].bl; break;
873                 case 2: l = m[pos->p.m].cl; break;
874                 }
875                 if (pos->p.o >= l) {
876                         pos->p.o = 0;
877                         do {
878                                 pos->p.s++;
879                                 if (pos->p.s > 2) {
880                                         pos->p.s = 0;
881                                         pos->p.m++;
882                                 }
883                         } while (invalid(pos->p.s, m[pos->p.m].type));
884                 } else
885                         break;
886         }
887         if (pos->p.m == -1 || m[pos->p.m].type == End) {
888                 struct elmnt e;
889                 e.start = NULL; e.len = 0;
890                 return e;
891         }
892         switch(pos->p.s) {
893         default: /* keep compiler happy */
894         case 0: return fm.list[m[pos->p.m].a + pos->p.o];
895         case 1: return fb.list[m[pos->p.m].b + pos->p.o];
896         case 2: return fa.list[m[pos->p.m].c + pos->p.o];
897         }
898 }
899
900 struct elmnt prev_melmnt(struct mpos *pos,
901                          struct file fm, struct file fb, struct file fa,
902                          struct merge *m)
903 {
904         pos->p.o--;
905         while (pos->p.m >=0 && pos->p.o < 0) {
906                 do {
907                         pos->p.s--;
908                         if (pos->p.s < 0) {
909                                 pos->p.s = 2;
910                                 pos->p.m--;
911                         }
912                 } while (pos->p.m >= 0 && invalid(pos->p.s, m[pos->p.m].type));
913                 if (pos->p.m>=0) {
914                         switch(pos->p.s) {
915                         case 0: pos->p.o = m[pos->p.m].al-1; break;
916                         case 1: pos->p.o = m[pos->p.m].bl-1; break;
917                         case 2: pos->p.o = m[pos->p.m].cl-1; break;
918                         }
919                 }
920         }
921         if (pos->p.m < 0) {
922                 struct elmnt e;
923                 e.start = NULL; e.len = 0;
924                 return e;
925         }
926         switch(pos->p.s) {
927         default: /* keep compiler happy */
928         case 0: return fm.list[m[pos->p.m].a + pos->p.o];
929         case 1: return fb.list[m[pos->p.m].b + pos->p.o];
930         case 2: return fa.list[m[pos->p.m].c + pos->p.o];
931         }
932 }
933
934 int visible(int mode, enum mergetype type, int stream)
935 {
936 #if 0
937         switch(side){
938         case 0: /* left - orig plus merge */
939                 switch(type) {
940                 case End: return A_NORMAL;
941                 case Unmatched: return stream == 0 ? a_unmatched : -1;
942                 case Unchanged: return stream == 0 ? a_common : -1;
943                 case Extraneous: return -1;
944                 case Changed: return stream == 0? a_delete:stream==1?-1:a_added;
945                 case Conflict: return stream == 0 ? a_unmatched : -1;
946                 case AlreadyApplied: return stream == 0 ? a_unmatched : -1;
947                 }
948                 break;
949         case 1: /* right - old plus new */
950                 switch(type) {
951                 case End: return A_NORMAL;
952                 case Unmatched: return -1;
953                 case Unchanged: return stream == 0 ? a_common : -1;
954                 case Extraneous: return stream==2 ? a_extra : -1;
955                 case Changed: return stream == 0? a_delete:stream==1?-1:a_added;
956                 case Conflict: return stream==0?-1:stream==1?(a_delete|A_UNDERLINE):a_added;
957                 case AlreadyApplied: return stream==0?-1:stream==1?a_delete:a_added;
958                 }
959         }
960         return 1;
961 #endif
962         if (mode == 0) return -1;
963         /* mode can be any combination of ORIG RESULT BEFORE AFTER */
964         switch(type) {
965         case End: /* The END is always visible */
966                 return A_NORMAL;
967         case Unmatched: /* Visible in ORIG and RESULT */
968                 if (mode & (ORIG|RESULT))
969                         return a_unmatched;
970                 break;
971         case Unchanged: /* visible everywhere, but only show stream 0 */
972                 if (stream == 0) return a_common;
973                 break;
974         case Extraneous: /* stream 2 is visible in BEFORE and AFTER */
975                 if ((mode & (BEFORE|AFTER))
976                     && stream == 2)
977                         return a_extra;
978                 break;
979         case Changed: /* stream zero visible ORIG and BEFORE, stream 2 elsewhere */
980                 if (stream == 0 &&
981                     (mode & (ORIG|BEFORE)))
982                         return a_delete;
983                 if (stream == 2 &&
984                     (mode & (RESULT|AFTER)))
985                         return a_added;
986                 break;
987         case Conflict:
988                 switch(stream) {
989                 case 0:
990                         if (mode & (ORIG|RESULT))
991                                 return a_unmatched | A_REVERSE;
992                         break;
993                 case 1:
994                         if (mode & BEFORE)
995                                 return a_extra | A_UNDERLINE;
996                         break;
997                 case 2:
998                         if (mode & (AFTER|RESULT))
999                                 return a_added | A_UNDERLINE;
1000                         break;
1001                 }
1002                 break;
1003         case AlreadyApplied:
1004                 switch(stream) {
1005                 case 0:
1006                         if (mode & (ORIG|RESULT))
1007                                 return a_already;
1008                         break;
1009                 case 1:
1010                         if (mode & BEFORE)
1011                                 return a_delete | A_UNDERLINE;
1012                         break;
1013                 case 2:
1014                         if (mode & AFTER)
1015                                 return a_added | A_UNDERLINE;
1016                         break;
1017                 }
1018                 break;
1019         }
1020         return -1;
1021 }
1022
1023
1024 int check_line(struct mpos pos, struct file fm, struct file fb, struct file fa,
1025                struct merge *m, int mode);
1026
1027 void next_mline(struct mpos *pos, struct file fm, struct file fb, struct file fa,
1028               struct merge *m, int mode)
1029 {
1030         int mask;
1031         do {
1032                 struct mp prv;
1033                 int mode2;
1034
1035                 prv = pos->p;
1036 #if 0
1037                 if (pos->p.m < 0 || m[pos->p.m].type == End) {
1038                         if (pos->side == -1) {
1039                                 pos->side = 1;
1040                                 pos->hi = prv;
1041                                 pos->p = pos->lo;
1042                                 continue;
1043                         } else
1044                                 return;
1045                 }
1046 #endif
1047                 while (1) {
1048                         struct elmnt e = next_melmnt(pos, fm,fb,fa,m);
1049                         if (e.start == NULL)
1050                                 break;
1051                         if (ends_mline(e) &&
1052                             visible(mode, m[pos->p.m].type, pos->p.s) >= 0)
1053                                 break;
1054                 }
1055                 mode2 = check_line(*pos, fm,fb,fa,m,mode);
1056
1057                 if (pos->side == 1 && !(mode2 & CHANGES))
1058                         /* left a diff-set */
1059                         pos->side = 0;
1060                 else if (pos->side == -1 && !(mode2 & CHANGES)) {
1061                         /*flip from '-' to '+' - need to ensure still visible */
1062                         pos->side = 1;
1063                         pos->hi = prv;
1064                         pos->p = pos->lo;
1065                 } else if (pos->side == 0 && (mode2 & CHANGES)) {
1066                         /* entered a diff-set */
1067                         pos->side = -1;
1068                         pos->lo = pos->p;
1069                 }
1070                 mask = ORIG|RESULT|BEFORE|AFTER|CHANGES|CHANGED;
1071                 if (pos->side == 1)
1072                         mask &= ~(ORIG|BEFORE);
1073                 if (pos->side == -1)
1074                         mask &= ~(RESULT|AFTER);
1075         } while (visible(mode&mask, m[pos->p.m].type, pos->p.s) < 0);
1076
1077 }
1078
1079 void prev_mline(struct mpos *pos, struct file fm, struct file fb, struct file fa,
1080                 struct merge *m, int mode)
1081 {
1082         int mask;
1083         do {
1084                 struct mp prv;
1085                 int mode2;
1086
1087                 prv = pos->p;
1088                 if (pos->p.m < 0)
1089                         return;
1090                 while(1) {
1091                         struct elmnt e = prev_melmnt(pos, fm,fb,fa,m);
1092                         if (e.start == NULL)
1093                                 break;
1094                         if (ends_mline(e) &&
1095                             visible(mode, m[pos->p.m].type, pos->p.s) >= 0)
1096                                 break;
1097                 }
1098                 mode2 = check_line(*pos, fm,fb,fa,m,mode);
1099
1100                 if (pos->side == -1 && !(mode2 & CHANGES))
1101                         /* we have stepped out of a diff-set */
1102                         pos->side = 0;
1103                 else if (pos->side == 1 && !(mode2 & CHANGES)) {
1104                         /* flipped from '+' to '-' */
1105                         pos->side = -1;
1106                         pos->lo = prv;
1107                         pos->p = pos->hi;
1108                 } else if (pos->side == 0 && (mode2 & CHANGES)) {
1109                         /* entered a diffset */
1110                         pos->side = 1;
1111                         pos->hi = pos->p;
1112                 }
1113                 mask = ORIG|RESULT|BEFORE|AFTER|CHANGES|CHANGED;
1114                 if (pos->side == 1)
1115                         mask &= ~(ORIG|BEFORE);
1116                 if (pos->side == -1)
1117                         mask &= ~(RESULT|AFTER);
1118         } while (visible(mode&mask, m[pos->p.m].type, pos->p.s) < 0);
1119 }
1120
1121
1122 void blank(int row, int start, int cols, int attr)
1123 {
1124         (void)attrset(attr);
1125         move(row,start);
1126         while (cols-- > 0)
1127                 addch(' ');
1128 }
1129
1130 /* Checkline determines how many screen lines are needed to display
1131  * a file line.
1132  * Options are:
1133  *     - one line that is before/original
1134  *     - one line that is after/result
1135  *     - one line that is unchanged/unmatched/extraneous
1136  *     - two lines, but only one on the left.
1137  *     - two lines, one before and one after.
1138  * CLARIFY/CORRECT this.
1139  */
1140 int check_line(struct mpos pos, struct file fm, struct file fb, struct file fa,
1141                struct merge *m, int mode)
1142 {
1143         int rv = 0;
1144         struct elmnt e;
1145
1146         if (visible(ORIG, m[pos.p.m].type, pos.p.s) >= 0)
1147                 rv |= ORIG;
1148         if (visible(RESULT, m[pos.p.m].type, pos.p.s) >= 0)
1149                 rv |= RESULT;
1150         if (visible(BEFORE, m[pos.p.m].type, pos.p.s) >= 0)
1151                 rv |= BEFORE;
1152         if (visible(AFTER, m[pos.p.m].type, pos.p.s) >= 0)
1153                 rv |= AFTER;
1154
1155         do {
1156                 if (m[pos.p.m].type == Changed)
1157                         rv |= CHANGED | CHANGES;
1158                 else if ((m[pos.p.m].type == AlreadyApplied ||
1159                           m[pos.p.m].type == Conflict))
1160                         rv |= CHANGES;
1161                 e = prev_melmnt(&pos, fm,fb,fa,m);
1162         } while (e.start != NULL &&
1163                  (!ends_mline(e) || visible(mode, m[pos.p.m].type, pos.p.s)==-1));
1164
1165         return rv & (mode | CHANGED | CHANGES);
1166 }
1167
1168 int mcontains(struct mpos pos,
1169               struct file fm, struct file fb, struct file fa, struct merge *m,
1170               int mode, char *search)
1171 {
1172         /* See if and of the files, between start of this line and here,
1173          * contain the search string
1174          */
1175         struct elmnt e;
1176         int len = strlen(search);
1177         do {
1178                 e = prev_melmnt(&pos, fm,fb,fa,m);
1179                 if (e.start) {
1180                         int i;
1181                         for (i=0; i<e.len; i++)
1182                                 if (strncmp(e.start+i, search, len)==0)
1183                                         return 1;
1184                 }
1185         } while (e.start != NULL &&
1186                  (!ends_mline(e) || visible(mode, m[pos.p.m].type, pos.p.s)==-1));
1187         return 0;
1188 }
1189
1190 void draw_mside(int mode, int row, int offset, int start, int cols,
1191                 struct file fm, struct file fb, struct file fa, struct merge *m,
1192                 struct mpos pos,
1193                 int target, int *colp)
1194 {
1195         /* find previous visible newline, or start of file */
1196         struct elmnt e;
1197         int col = 0;
1198         do {
1199                 e = prev_melmnt(&pos, fm,fb,fa,m);
1200         } while (e.start != NULL &&
1201                  (!ends_mline(e) || visible(mode, m[pos.p.m].type, pos.p.s)==-1));
1202
1203         while (1) {
1204                 unsigned char *c;
1205                 int l;
1206                 e = next_melmnt(&pos, fm,fb,fa,m);
1207                 if (e.start == NULL ||
1208                     (ends_mline(e) && visible(mode, m[pos.p.m].type, pos.p.s) != -1)) {
1209                         if (colp) *colp = col;
1210                         if (col < start) col = start;
1211                         if (e.start && e.start[0] == 0) {
1212                                 (void)attrset(visible(mode, m[pos.p.m].type, pos.p.s));
1213                                 mvaddstr(row, col-start+offset, "SEP");
1214                                 col += 3;
1215                         }
1216                         blank(row, col-start+offset, start+cols-col, e.start?visible(mode, m[pos.p.m].type, pos.p.s):A_NORMAL );
1217                         return;
1218                 }
1219                 if (visible(mode, m[pos.p.m].type, pos.p.s) == -1) {
1220                         continue;
1221                 }
1222                 if (e.start[0] == 0)
1223                         continue;
1224                 (void)attrset(visible(mode, m[pos.p.m].type, pos.p.s));
1225                 c = (unsigned char *)e.start;
1226                 l = e.len;
1227                 while(l) {
1228                         if (*c >= ' ' && *c != 0x7f) {
1229                                 if (col >= start && col < start+cols)
1230                                         mvaddch(row, col-start+offset, *c);
1231                                 col++;
1232                         } else if (*c == '\t') {
1233                                 do {
1234                                         if (col >= start && col < start+cols)
1235                                                 mvaddch(row, col-start+offset, ' ');
1236                                         col++;
1237                                 } while ((col&7)!= 0);
1238                         } else {
1239                                 if (col >= start && col < start+cols)
1240                                         mvaddch(row, col-start+offset, '?');
1241                                 col++;
1242                         }
1243                         c++;
1244                         if (colp && target <= col) {
1245                                 *colp = col;
1246                                 colp = NULL;
1247                         }
1248                         l--;
1249                 }
1250         }
1251 }
1252
1253 void draw_mline(int row, struct mpos pos,
1254                 struct file fm, struct file fb, struct file fa,
1255                 struct merge *m,
1256                 int start, int cols, int mode,
1257                 int target, int *colp)
1258 {
1259         /*
1260          * Draw the left and right images of this line
1261          * One side might be a_blank depending on the
1262          * visibility of this newline
1263          * If Changed is found, return ORIG|RESULT | BEFORE|AFTER,
1264          * If AlreadyApplied or Conflict, return BEFORE|AFTER
1265          */
1266         int lcols, rcols;
1267         lcols = (cols-1)/2-1;
1268         rcols = cols - lcols - 3;
1269
1270         attrset(A_STANDOUT);
1271         mvaddch(row, lcols+1, '|');
1272
1273         attrset(A_NORMAL);
1274         if (!(mode & CHANGES)) {
1275                 mvaddch(row, 0, ' ');
1276                 mvaddch(row, lcols+2, ' ');
1277         } else if (mode & (ORIG|BEFORE)) {
1278                 mvaddch(row, 0, '-');
1279                 mvaddch(row, lcols+2, '-');
1280         } else {
1281                 mvaddch(row, 0, '+');
1282                 mvaddch(row, lcols+2, '+');
1283         }
1284
1285         if (visible(mode&(ORIG|RESULT), m[pos.p.m].type, pos.p.s) >= 0)
1286                 /* visible on left */
1287                 draw_mside(mode&(ORIG|RESULT), row, 1, start, lcols,
1288                            fm,fb,fa,m, pos, target, colp);
1289         else {
1290                 blank(row, 0, lcols+1, a_void);
1291                 if (colp) *colp = 0;
1292         }
1293
1294         if (visible(mode&(BEFORE|AFTER), m[pos.p.m].type, pos.p.s) >= 0)
1295                 /* visible on right */
1296                 draw_mside(mode&(BEFORE|AFTER), row, lcols+3, start, rcols,
1297                            fm,fb,fa,m, pos, 0, NULL);
1298         else
1299                 blank(row, lcols+2, rcols+1, a_void);
1300 }
1301
1302 extern void cleanlist(struct file a, struct file b, struct csl *list);
1303
1304 void merge_window(struct plist *p, FILE *f, int reverse)
1305 {
1306         /* display the merge in two side-by-side
1307          * panes.
1308          * left side shows diff between original and new
1309          * right side shows the requested patch
1310          *
1311          * Unmatched: c_unmatched - left only
1312          * Unchanged: c_normal - left and right
1313          * Extraneous: c_extra - right only
1314          * Changed-a: c_changed - left and right
1315          * Changed-c: c_new - left and right
1316          * AlreadyApplied-b: c_old - right only
1317          * AlreadyApplied-c: c_applied - left and right
1318          * Conflict-a: ?? left only
1319          * Conflict-b: ?? left and right
1320          * Conflict-c: ??
1321          *
1322          * A Conflict is displayed as the original in the
1323          * left side, and the highlighted diff in the right.
1324          *
1325          * Newlines are the key to display.
1326          * 'pos' is always a newline (or eof).
1327          * For each side that this newline is visible on, we
1328          * rewind the previous newline visible on this side, and
1329          * the display the stuff in between
1330          *
1331          * A 'position' is an offset in the merger, a stream
1332          * choice (a,b,c - some aren't relevant) and an offset in
1333          * that stream
1334          */
1335
1336         struct stream sm, sb, sa, sp; /* main, before, after, patch */
1337         struct file fm, fb, fa;
1338         struct csl *csl1, *csl2;
1339         struct ci ci;
1340         char buf[100];
1341         int ch;
1342         int refresh = 2;
1343         int rows,cols;
1344         int i, c;
1345         int mode = ORIG|RESULT | BEFORE|AFTER;
1346
1347         int row,start = 0;
1348         int trow;
1349         int col=0, target=0;
1350         struct mpos pos;
1351         struct mpos tpos, toppos, botpos;
1352         int toprow,botrow;
1353         int mode2;
1354         int meta = 0, tmeta;
1355         int num= -1, tnum;
1356         char search[80];
1357         int searchlen = 0;
1358         int search_notfound = 0;
1359         int searchdir;
1360         struct search_anchor {
1361                 struct search_anchor *next;
1362                 struct mpos pos;
1363                 int notfound;
1364                 int row, col, searchlen;
1365         } *anchor = NULL;
1366
1367         sp = load_segment(f, p->start, p->end);
1368         if (reverse)
1369                 ch = split_patch(sp, &sa, &sb);
1370         else
1371                 ch = split_patch(sp, &sb, &sa);
1372
1373         sm = load_file(p->file);
1374         fm = split_stream(sm, ByWord, 0);
1375         fb = split_stream(sb, ByWord, 0);
1376         fa = split_stream(sa, ByWord, 0);
1377
1378         csl1 = pdiff(fm, fb, ch);
1379         csl2 = diff(fb, fa);
1380 #if 0
1381         cleanlist(fm, fb, csl1);
1382         cleanlist(fb, fa, csl2);
1383 #endif
1384
1385         ci = make_merger(fm, fb, fa, csl1, csl2, 0, 1);
1386
1387         row = 1;
1388         pos.p.m = 0; /* merge node */
1389         pos.p.s = 0; /* stream number */
1390         pos.p.o = -1; /* offset */
1391         next_mline(&pos, fm,fb,fa,ci.merger, mode);
1392         mode2 = check_line(pos, fm,fb,fa, ci.merger, mode);
1393         if (!(mode2 & CHANGES))
1394                 pos.side = 0;
1395         else {
1396                 if (mode2 & (ORIG|BEFORE))
1397                         pos.side = -1;
1398                 else
1399                         pos.side = 1;
1400                 pos.lo = pos.p;
1401                 tpos = pos;
1402                 do {
1403                         pos.hi = tpos.p;
1404                         next_mline(&tpos, fm,fb,fa,ci.merger, mode);
1405                         mode2 = check_line(tpos, fm,fb,fa, ci.merger, mode);
1406                 } while ((mode2 & CHANGES) && ci.merger[tpos.p.m].type != End);
1407         }
1408
1409         while(1) {
1410                 if (refresh == 2) {
1411                         clear();
1412                         sprintf(buf, "File: %s%s\n", p->file,reverse?" - reversed":"");
1413                         attrset(A_BOLD); mvaddstr(0,0,buf);
1414                         clrtoeol();
1415                         attrset(A_NORMAL);
1416                         refresh = 1;
1417                 }
1418                 if (row < 1 || row >= rows)
1419                         refresh = 1;
1420
1421
1422                 if (refresh) {
1423                         getmaxyx(stdscr, rows, cols);
1424                         rows--; /* keep last row clear */
1425                         if (row < -3)
1426                                 row = (rows-1)/2+1;
1427                         if (row < 1)
1428                                 row = 1;
1429                         if (row > rows+3)
1430                                 row = (rows-1)/2+1;
1431                         if (row >= rows)
1432                                 row = rows-1;
1433                 }
1434
1435                 /* Always refresh the line */
1436                 while (start > target) { start -= 8; refresh = 1;}
1437                 if (start < 0) start = 0;
1438         retry:
1439                 mode2 = check_line(pos, fm,fb,fa,ci.merger,mode);
1440                 if ((pos.side <= 0) && (mode2 & (ORIG|BEFORE)))
1441                         draw_mline(row,pos,fm,fb,fa,ci.merger,start,cols, mode2&(ORIG|BEFORE|CHANGED|CHANGES), target, &col);
1442                 else if (pos.side > 0 && (mode2 & (RESULT|AFTER))) {
1443                         if (mode2 & CHANGED)
1444                                 draw_mline(row,pos,fm,fb,fa,ci.merger,start,cols, mode2&(RESULT|AFTER|CHANGED|CHANGES), target, &col);
1445                         else if (mode2 & CHANGES)
1446                                 draw_mline(row,pos,fm,fb,fa,ci.merger,start,cols, mode2&(AFTER|CHANGED|CHANGES), target, &col);
1447                 }
1448                 if (col > (cols-3)/2+start) {
1449                         start += 8;
1450                         refresh = 1;
1451                         goto retry;
1452                 }
1453                 if (col < start) {
1454                         start -= 8;
1455                         refresh = 1;
1456                         if (start < 0) start = 0;
1457                         goto retry;
1458                 }
1459                 if (refresh) {
1460                         refresh = 0;
1461                         tpos = pos;
1462
1463                         for (i=row-1; i>=1 && tpos.p.m >= 0; ) {
1464                                 prev_mline(&tpos, fm,fb,fa,ci.merger, mode);
1465                                 mode2 = check_line(tpos, fm,fb,fa, ci.merger, mode);
1466
1467                                 if (tpos.side == 1 &&
1468                                     (mode2 & (RESULT|AFTER)) &&
1469                                     (mode2 & (CHANGED)))
1470                                         draw_mline(i--,tpos,fm,fb,fa,ci.merger,start,cols, mode2&(RESULT|AFTER|CHANGED|CHANGES), 0, NULL);
1471                                 else if (tpos.side == 1 &&
1472                                          (mode2 & (RESULT|AFTER)) &&
1473                                     (mode2 & (CHANGES)))
1474                                         draw_mline(i--,tpos,fm,fb,fa,ci.merger,start,cols, mode2&(AFTER|CHANGED|CHANGES), 0, NULL);
1475                                 else if ((tpos.side == 0 || tpos.side == -1) && (mode2 & (ORIG|BEFORE)))
1476                                         draw_mline(i--,tpos,fm,fb,fa,ci.merger,start,cols, mode2&(ORIG|BEFORE|CHANGED|CHANGES), 0, NULL);
1477                         }
1478                         toppos = tpos; toprow = i;
1479                         while (i >= 1)
1480                                 blank(i--, 0, cols, a_void);
1481                         tpos = pos;
1482                         for (i=row; i<rows && ci.merger[tpos.p.m].type != End; ) {
1483                                 mode2 = check_line(tpos, fm,fb,fa,ci.merger,mode);
1484                                 if ((tpos.side <= 0) && (mode2 & (ORIG|BEFORE)))
1485                                         draw_mline(i++,tpos,fm,fb,fa,ci.merger,start,cols, mode2&(ORIG|BEFORE|CHANGED|CHANGES), 0, NULL);
1486                                 else if (tpos.side > 0 && (mode2 & (RESULT|AFTER))) {
1487                                         if (mode2 & CHANGED)
1488                                                 draw_mline(i++,tpos,fm,fb,fa,ci.merger,start,cols, mode2&(RESULT|AFTER|CHANGED|CHANGES), 0, NULL);
1489                                         else if (mode2 & CHANGES)
1490                                                 draw_mline(i++,tpos,fm,fb,fa,ci.merger,start,cols, mode2&(AFTER|CHANGED|CHANGES), 0, NULL);
1491                                 }
1492                                 next_mline(&tpos, fm,fb,fa,ci.merger, mode);
1493                         }
1494                         botpos = tpos; botrow = i;
1495                         while (i<rows)
1496                                 blank(i++, 0, cols, a_void);
1497                 } else {
1498                 }
1499 #define META(c) ((c)|0x1000)
1500 #define SEARCH(c) ((c)|0x2000)
1501                 move(rows,0);
1502                 attrset(A_NORMAL);
1503                 if (num>=0) { char buf[10]; sprintf(buf, "%d ", num); addstr(buf);}
1504                 if (meta & META(0)) addstr("ESC...");
1505                 if (meta & SEARCH(0)) {
1506                         if (searchdir) addstr("Backwards ");
1507                         addstr("Search: ");
1508                         addstr(search);
1509                         if (search_notfound)
1510                                 addstr(" - Not Found.");
1511                         search_notfound = 0;
1512                 }
1513                 clrtoeol();
1514                 move(row,col-start);
1515                 c = getch();
1516                 tmeta = meta; meta = 0;
1517                 tnum = num; num = -1;
1518                 switch(c | tmeta) {
1519                 case 27: /* escape */
1520                 case META(27):
1521                         meta = META(0);
1522                         break;
1523                 case META('<'): /* start of file */
1524                 start:
1525                         tpos = pos; row++;
1526                         do {
1527                                 pos = tpos; row--;
1528                                 prev_mline(&tpos, fm,fb,fa,ci.merger,mode);
1529                         } while (tpos.p.m >= 0);
1530                         if (row <= 0)
1531                                 row = 0;
1532                         break;
1533                 case META('>'): /* end of file */
1534                 case 'G':
1535                         if (tnum >=0) goto start;
1536                         tpos = pos; row--;
1537                         do {
1538                                 pos = tpos; row++;
1539                                 next_mline(&tpos, fm,fb,fa,ci.merger,mode);
1540                         } while (ci.merger[tpos.p.m].type != End);
1541                         if (row >= rows)
1542                                 row = rows;
1543                         break;
1544                 case '0' ... '9':
1545                         if (tnum < 0) tnum = 0;
1546                         num = tnum*10 + (c-'0');
1547                         break;
1548                 case 'q':
1549                         return;
1550
1551                 case '/':
1552                 case 'S'-64:
1553                         /* incr search forward */
1554                         meta = SEARCH(0);
1555                         searchlen = 0;
1556                         search[searchlen] = 0;
1557                         searchdir = 0;
1558                         break;
1559                 case '\\':
1560                 case 'R'-64:
1561                         /* incr search backwards */
1562                         meta = SEARCH(0);
1563                         searchlen = 0;
1564                         search[searchlen] = 0;
1565                         searchdir = 1;
1566                         break;
1567                 case SEARCH('G'-64):
1568                 case SEARCH('S'-64):
1569                         /* search again */
1570                         meta = SEARCH(0);
1571                         tpos = pos; trow = row;
1572                         if (searchdir) {
1573                                 trow--;
1574                                 prev_mline(&tpos, fm,fb,fa,ci.merger,mode);
1575                         } else {
1576                                 trow++;
1577                                 next_mline(&tpos, fm,fb,fa,ci.merger,mode);
1578                         }
1579                         goto search_again;
1580
1581                 case SEARCH('H'-64):
1582                         meta = SEARCH(0);
1583                         if (anchor) {
1584                                 struct search_anchor *a;
1585                                 a = anchor;
1586                                 anchor = a->next;
1587                                 free(a);
1588                         }
1589                         if (anchor) {
1590                                 struct search_anchor *a;
1591                                 a = anchor;
1592                                 anchor = a->next;
1593                                 pos = a->pos;
1594                                 row = a->row;
1595                                 col = a->col;
1596                                 search_notfound = a->notfound;
1597                                 searchlen = a->searchlen;
1598                                 search[searchlen] = 0;
1599                                 free(a);
1600                                 refresh = 1;
1601                         }
1602                         break;
1603                 case SEARCH(' ') ... SEARCH('~'):
1604                 case SEARCH('\t'):
1605                         meta = SEARCH(0);
1606                         if (searchlen < sizeof(search)-1)
1607                                 search[searchlen++] = c & (0x7f);
1608                         search[searchlen] = 0;
1609                         tpos = pos; trow = row;
1610                 search_again:
1611                         search_notfound = 1;
1612                         do {
1613                                 if (mcontains(tpos, fm,fb,fa,ci.merger,mode,search)) {
1614                                         pos = tpos;
1615                                         row = trow;
1616                                         search_notfound = 0;
1617                                         break;
1618                                 }
1619                                 if (searchdir) {
1620                                         trow--;
1621                                         prev_mline(&tpos, fm,fb,fa,ci.merger,mode);
1622                                 } else {
1623                                         trow++;
1624                                         next_mline(&tpos, fm,fb,fa,ci.merger,mode);
1625                                 }
1626                         } while (tpos.p.m >= 0 && ci.merger[tpos.p.m].type != End);
1627
1628                         break;
1629                 case 'L'-64:
1630                         refresh = 2;
1631                         if (toprow >= 1) row -= (toprow+1);
1632                         break;
1633
1634                 case 'V'-64: /* page down */
1635                         pos = botpos;
1636                         if (botrow < rows)
1637                                 row = botrow;
1638                         else
1639                                 row = 2;
1640                         refresh = 1;
1641                         break;
1642                 case META('v'): /* page up */
1643                         pos = toppos;
1644                         if (toprow >= 1)
1645                                 row = toprow+1;
1646                         else
1647                                 row = rows-2;
1648                         refresh = 1;
1649                         break;
1650
1651                 case 'j':
1652                 case 'n':
1653                 case 'N'-64:
1654                 case KEY_DOWN:
1655                         if (tnum < 0) tnum = 1;
1656                         for (; tnum > 0 ; tnum--) {
1657                                 tpos = pos;
1658                                 next_mline(&tpos, fm,fb,fa,ci.merger, mode);
1659                                 if (ci.merger[tpos.p.m].type != End) {
1660                                         pos = tpos;
1661                                         row++;
1662                                 }
1663                         }
1664                         break;
1665                 case 'N':
1666                         /* Next diff */
1667                         tpos = pos; row--;
1668                         do {
1669                                 pos = tpos; row++;
1670                                 next_mline(&tpos, fm,fb,fa,ci.merger, mode);
1671                         } while (pos.side != 0 && ci.merger[tpos.p.m].type != End);
1672                         tpos = pos; row--;
1673                         do {
1674                                 pos = tpos; row++;
1675                                 next_mline(&tpos, fm,fb,fa,ci.merger, mode);
1676                         } while (pos.side == 0 && ci.merger[tpos.p.m].type != End);
1677
1678                         break;
1679                 case 'P':
1680                         /* Previous diff */
1681                         tpos = pos; row++;
1682                         do {
1683                                 pos = tpos; row--;
1684                                 prev_mline(&tpos, fm,fb,fa,ci.merger, mode);
1685                         } while (tpos.side == 0 && tpos.p.m >= 0);
1686                         tpos = pos; row++;
1687                         do {
1688                                 pos = tpos; row--;
1689                                 prev_mline(&tpos, fm,fb,fa,ci.merger, mode);
1690                         } while (tpos.side != 0 && tpos.p.m >= 0);
1691
1692                         break;
1693
1694                 case 'k':
1695                 case 'p':
1696                 case 'P'-64:
1697                 case KEY_UP:
1698                         if (tnum < 0) tnum = 1;
1699                         for (; tnum > 0 ; tnum--) {
1700                                 tpos = pos;
1701                                 prev_mline(&tpos, fm,fb,fa,ci.merger, mode);
1702                                 if (tpos.p.m >= 0) {
1703                                         pos = tpos;
1704                                         row--;
1705                                 }
1706                         }
1707                         break;
1708
1709                 case KEY_LEFT:
1710                 case 'h':
1711                         /* left */
1712                         target = col - 1;
1713                         if (target < 0) target = 0;
1714                         break;
1715                 case KEY_RIGHT:
1716                 case 'l':
1717                         /* right */
1718                         target = col + 1;
1719                         break;
1720
1721                 case '^':
1722                 case 'A'-64:
1723                         /* Start of line */
1724                         target = 0;
1725                         break;
1726                 case '$':
1727                 case 'E'-64:
1728                         /* End of line */
1729                         target = 1000;
1730                         break;
1731
1732                 case 'a': /* 'after' view in patch window */
1733                         if (mode & AFTER)
1734                                 mode &= ~BEFORE;
1735                         else
1736                                 mode |= AFTER;
1737                         refresh = 1;
1738                         break;
1739                 case 'b': /* 'before' view in patch window */
1740                         if (mode & BEFORE)
1741                                 mode &= ~AFTER;
1742                         else
1743                                 mode |= BEFORE;
1744                         refresh = 1;
1745                         break;
1746                 case 'o': /* 'original' view in the merge window */
1747                         if (mode & ORIG)
1748                                 mode &= ~RESULT;
1749                         else
1750                                 mode |= ORIG;
1751                         refresh = 1;
1752                         break;
1753                 case 'r': /* the 'result' view in the merge window */
1754                         if (mode & RESULT)
1755                                 mode &= ~ORIG;
1756                         else
1757                                 mode |= RESULT;
1758                         refresh = 1;
1759                         break;
1760 #if 0
1761                 case 'h':
1762                 case KEY_LEFT:
1763                         if (start > 0) start--;
1764                         refresh = 1;
1765                         break;
1766                 case 'l':
1767                 case KEY_RIGHT:
1768                         if (start < cols) start++;
1769                         refresh = 1;
1770                         break;
1771 #endif
1772                 }
1773
1774                 if (meta == SEARCH(0)) {
1775                         if (anchor == NULL ||
1776                             !same_mpos(anchor->pos, pos) ||
1777                             anchor->searchlen != searchlen ||
1778                             anchor->col != col) {
1779                                 struct search_anchor *a = malloc(sizeof(*a));
1780                                 a->pos = pos;
1781                                 a->row = row;
1782                                 a->col = col;
1783                                 a->searchlen = searchlen;
1784                                 a->notfound = search_notfound;
1785                                 a->next = anchor;
1786                                 anchor = a;
1787                         }
1788                 } else {
1789                         while (anchor) {
1790                                 struct search_anchor *a = anchor;
1791                                 anchor = a->next;
1792                                 free(a);
1793                         }
1794                 }
1795         }
1796 }
1797
1798 void main_window(struct plist *pl, int n, FILE *f, int reverse)
1799 {
1800         /* The main window lists all files together with summary information:
1801          * number of chunks, number of wiggles, number of conflicts.
1802          * The list is scrollable
1803          * When a entry is 'selected', we switch to the 'file' window
1804          * The list can be condensed by removing files with no conflict
1805          * or no wiggles, or removing subdirectories
1806          *
1807          * We record which file in the list is 'current', and which
1808          * screen line it is on.  We try to keep things stable while
1809          * moving.
1810          *
1811          * Counts are printed before the name using at most 2 digits.
1812          * Numbers greater than 99 are XX
1813          * Ch Wi Co File
1814          * 27 5   1 drivers/md/md.c
1815          *
1816          * A directory show the sum in all children.
1817          *
1818          * Commands:
1819          *  select:  enter, space, mouseclick
1820          *      on file, go to file window
1821          *      on directory, toggle open
1822          *  up:  k, p, control-p uparrow
1823          *      Move to previous open object
1824          *  down: j, n, control-n, downarrow
1825          *      Move to next open object
1826          *
1827          */
1828         int pos=0; /* position in file */
1829         int row=1; /* position on screen */
1830         int rows; /* size of screen in rows */
1831         int cols;
1832         int tpos, i;
1833         int refresh = 2;
1834         int c=0;
1835
1836         while(1) {
1837                 if (refresh == 2) {
1838                         clear(); attrset(0);
1839                         attron(A_BOLD);
1840                         mvaddstr(0,0,"Ch Wi Co Patched Files");
1841                         move(2,0);
1842                         attroff(A_BOLD);
1843                         refresh = 1;
1844                 }
1845                 if (row <1  || row >= rows)
1846                         refresh = 1;
1847                 if (refresh) {
1848                         refresh = 0;
1849                         getmaxyx(stdscr, rows, cols);
1850                         if (row >= rows +3)
1851                                 row = (rows+1)/2;
1852                         if (row >= rows)
1853                                 row = rows-1;
1854                         tpos = pos;
1855                         for (i=row; i>1; i--) {
1856                                 tpos = get_prev(tpos, pl, n);
1857                                 if (tpos == -1) {
1858                                         row = row - i + 1;
1859                                         break;
1860                                 }
1861                         }
1862                         /* Ok, row and pos could be trustworthy now */
1863                         tpos = pos;
1864                         for (i=row; i>=1; i--) {
1865                                 draw_one(i, &pl[tpos], f, reverse);
1866                                 tpos = get_prev(tpos, pl, n);
1867                         }
1868                         tpos = pos;
1869                         for (i=row+1; i<rows; i++) {
1870                                 tpos = get_next(tpos, pl, n);
1871                                 if (tpos >= 0)
1872                                         draw_one(i, &pl[tpos], f, reverse);
1873                                 else
1874                                         draw_one(i, NULL, f, reverse);
1875                         }
1876                 }
1877                 {char bb[20]; sprintf(bb,"%d", c); mvaddstr(0, 70, bb); clrtoeol();}
1878                 move(row, 9);
1879                 c = getch();
1880                 switch(c) {
1881                 case 'j':
1882                 case 'n':
1883                 case 'N':
1884                 case 'N'-64:
1885                 case KEY_DOWN:
1886                         tpos = get_next(pos, pl, n);
1887                         if (tpos >= 0) {
1888                                 pos = tpos;
1889                                 row++;
1890                         }
1891                         break;
1892                 case 'k':
1893                 case 'p':
1894                 case 'P':
1895                 case 'P'-64:
1896                 case KEY_UP:
1897                         tpos = get_prev(pos, pl, n);
1898                         if (tpos >= 0) {
1899                                 pos = tpos;
1900                                 row--;
1901                         }
1902                         break;
1903
1904                 case ' ':
1905                 case 13:
1906                         if (pl[pos].end == 0) {
1907                                 pl[pos].open = ! pl[pos].open;
1908                                 refresh = 1;
1909                         } else {
1910                                 /* diff_window(&pl[pos], f); */
1911                                 merge_window(&pl[pos],f,reverse);
1912                                 refresh = 2;
1913                         }
1914                         break;
1915                 case 27: /* escape */
1916                         mvaddstr(0,70,"ESC..."); clrtoeol();
1917                         c = getch();
1918                         switch(c) {
1919                         }
1920                         break;
1921                 case 'q':
1922                         return;
1923
1924                 case KEY_RESIZE:
1925                         refresh = 2;
1926                         break;
1927                 }
1928         }
1929 }
1930
1931
1932 int vpatch(int argc, char *argv[], int strip, int reverse, int replace)
1933 {
1934         /* NOT argv[0] is first arg... */
1935         int n = 0;
1936         FILE *f = NULL;
1937         FILE *in = stdin;
1938         struct plist *pl;
1939
1940         if (argc >=1) {
1941                 in = fopen(argv[0], "r");
1942                 if (!in) {
1943                         printf("No %s\n", argv[0]);
1944                         exit(1);
1945                 }
1946         } else {
1947                 in = stdin;
1948         }
1949         if (lseek(fileno(in),0L, 1) == -1) {
1950                 f = tmpfile();
1951                 if (!f) {
1952                         fprintf(stderr, "Cannot create a temp file\n");
1953                         exit(1);
1954                 }
1955         }
1956
1957         pl = parse_patch(in, f, &n);
1958
1959         if (set_prefix(pl, n, strip) == 0) {
1960                 fprintf(stderr, "%s: aborting\n", Cmd);
1961                 exit(2);
1962         }
1963         pl = sort_patches(pl, &n);
1964
1965         if (f) {
1966                 if (fileno(in) == 0) {
1967                         close(0);
1968                         dup(2);
1969                 } else
1970                         fclose(in);
1971                 in = f;
1972         }
1973 #if 0
1974         int i;
1975         for (i=0; i<n ; i++) {
1976                 printf("%3d: %3d %2d/%2d %s\n", i, pl[i].parent, pl[i].prev, pl[i].next, pl[i].file);
1977         }
1978         exit(0);
1979 #endif
1980         signal(SIGINT, catch);
1981         signal(SIGQUIT, catch);
1982         signal(SIGTERM, catch);
1983         signal(SIGBUS, catch);
1984         signal(SIGSEGV, catch);
1985
1986         initscr(); cbreak(); noecho();
1987         start_color();
1988         use_default_colors();
1989         if (!has_colors()) {
1990                 a_delete = A_UNDERLINE;
1991                 a_added = A_BOLD;
1992                 a_common = A_NORMAL;
1993                 a_sep = A_STANDOUT;
1994                 a_already = A_STANDOUT;
1995         } else {
1996                 init_pair(1, COLOR_WHITE, COLOR_RED);
1997                 a_delete = COLOR_PAIR(1);
1998                 init_pair(2, COLOR_WHITE, COLOR_BLUE);
1999                 a_added = COLOR_PAIR(2);
2000                 a_common = A_NORMAL;
2001                 init_pair(3, COLOR_WHITE, COLOR_GREEN);
2002                 a_sep = COLOR_PAIR(3);
2003                 init_pair(4, COLOR_WHITE, COLOR_YELLOW);
2004                 a_void = COLOR_PAIR(4);
2005                 init_pair(5, COLOR_BLUE, -1);
2006                 a_unmatched = COLOR_PAIR(5);
2007                 init_pair(6, COLOR_CYAN, -1);
2008                 a_extra = COLOR_PAIR(6);
2009
2010                 init_pair(7, COLOR_BLACK, COLOR_CYAN);
2011                 a_already = COLOR_PAIR(7);
2012         }
2013         nonl(); intrflush(stdscr, FALSE); keypad(stdscr, TRUE);
2014         mousemask(ALL_MOUSE_EVENTS, NULL);
2015
2016         main_window(pl, n, in, reverse);
2017
2018         nocbreak();nl();endwin();
2019         return 0;
2020         exit(0);
2021 }
2022 #if 0
2023  WiggleVerbose=1 ~/work/wiggle/wiggle -mR fs/nfsd/nfs4callback.c .patches/removed/144NfsdV4-033 |less
2024 neilb@notabene:/home/src/mm$
2025
2026 ~/work/wiggle/wiggle -BR .patches/removed/144NfsdV4-033
2027
2028
2029 #endif